﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BConverter = System.BitConverter;

namespace ArgusPCLib.BitRepresentations
{
	/// <summary>
	/// http://en.wikipedia.org/wiki/Floating_point#Internal_representation
	/// </summary>
	public static class ArgusLibExtensions
	{
		const int ExponentBiasSingle = 127;
		const int SignificantBitsSingle = 23;
		const int ExponentBiasDouble = 1023;
		const int SignificantBitsDouble = 52;

		public static int GetBits(this float f, out int significant, out int exponent, out bool isSubnormal)
		{
			uint bits = BConverter.ToUInt32(BConverter.GetBytes(f), 0);
			exponent = (int)((bits & (0xFF << SignificantBitsSingle)) >> SignificantBitsSingle) - ExponentBiasSingle;
			uint sig = bits & (uint.MaxValue >> (32 - SignificantBitsSingle));
			isSubnormal = exponent < (1 - ExponentBiasSingle);
			if (isSubnormal)
			{
				uint mask = 1u << SignificantBitsSingle;
				for (int i = 0; i < SignificantBitsSingle; i++)
				{
					exponent--;
					sig <<= 1;
					if ((bits & mask) == mask)
						break;
				}
			}
			else
			{
				sig |= 1u << SignificantBitsSingle;
			}
			significant = (int)sig;
			return (int)bits;
		}

		public static int GetBits(this float f, out int significant, out int exponent)
		{
			bool isSubnormal;
			return f.GetBits(out significant, out exponent, out isSubnormal);
		}

		public static int GetSignificant(this float f)
		{
			int significant, exponent;
			bool isSubnormal;
			f.GetBits(out significant, out exponent, out isSubnormal);
			return significant;
		}

		public static int GetExponent(this float f)
		{
			int significant, exponent;
			bool isSubnormal;
			f.GetBits(out significant, out exponent, out isSubnormal);
			return exponent;
		}

		public static bool IsSubnormal(this float f)
		{
			int significant, exponent;
			bool isSubnormal;
			f.GetBits(out significant, out exponent, out isSubnormal);
			return isSubnormal;
		}

		public static long GetBits(this double d, out long significant, out int exponent)
		{
			bool isSubnormal;
			return d.GetBits(out significant, out exponent, out isSubnormal);
		}

		public static long GetBits(this double d, out long significant, out int exponent, out bool isSubnormal)
		{
			ulong bits = BConverter.ToUInt64(BConverter.GetBytes(d), 0);
			exponent = (int)((bits & (0x7FFul << SignificantBitsDouble)) >> SignificantBitsDouble) - ExponentBiasDouble;
			ulong sig = bits & (ulong.MaxValue >> (64 - SignificantBitsDouble));
			isSubnormal = exponent < (1 - ExponentBiasDouble);
			if (isSubnormal)
			{
				ulong mask = 1ul << SignificantBitsDouble;
				for (int i = 0; i < SignificantBitsDouble; i++)
				{
					exponent--;
					sig <<= 1;
					if ((bits & mask) == mask)
						break;
				}
			}
			else
			{
				sig |= 1ul << SignificantBitsDouble;
			}
			significant = (long)sig;
			return (long)bits;
		}

		public static long GetSignificant(this double d)
		{
			long significant;
			int exponent;
			bool isSubnormal;
			d.GetBits(out significant, out exponent, out isSubnormal);
			return significant;
		}

		public static int GetExponent(this double d)
		{
			long significant;
			int exponent;
			bool isSubnormal;
			d.GetBits(out significant, out exponent, out isSubnormal);
			return exponent;
		}

		public static bool IsSubnormal(this double d)
		{
			long significant;
			int exponent;
			bool isSubnormal;
			d.GetBits(out significant, out exponent, out isSubnormal);
			return isSubnormal;
		}
	}
}
